/*
    ****************************
    定义Http处理部分
    ****************************
*/

#ifndef CPPHTTPSERVER
#define CPPHTTPSERVER

#include "HTTPRelated.h"
#include "cppThreadPool.h"
#include "redis.h"
#include "urlEncoderAndDecoder.h"
#include "getProcessor.h"
#include "postProcessor.h"
#include "requestProcessor.h"
#include "reqProcessorFactory.h"


class HTTPServer;
#define HS (reinterpret_cast<HTTPServer*>(parser->data))


class HTTPServer
{
private:
    Redis* redis; //定义redis做缓存
    threadPool* pool;
    int port;
    int backlog;
    int serverSockfd{};
    int epollFd{};
    char* requestBuf; 
    bool UseRedis;
    bool UseMultiThread;
    // url解码
    urlEncoderAndDecoder* codec;

    // 临时保存HTTP请求头 键值对
    std::vector<std::string> headFields;
    std::vector<std::string> headValues;

    // HTTP 解析器
    llhttp_t httpParser{};
    llhttp_settings_t* httpParserSettings;
    
    // 地址和epoll
    struct sockaddr_in* serverAddress{};
    struct sockaddr_in* clientAddress{};
    struct epoll_event event{};
    struct epoll_event events[MAX_EVENTS]{};  //events:即wait event
    
    // 请求处理的父类指针
    std::shared_ptr<requestProcessor> requestProcessPtr;

private:
    // 定义HTTP解析回调函数
    void setCallbacks(llhttp_settings_t* settings);

    // 绑定端口并监听
    int getServerSocketAndListen();

private:
    /* 
        llhttp解析器回调函数部分，说明：
        llhttp 使用回调钩子的设计思想，初始化解析器的时候，
        可以设置解析类型，是请求或响应报文，
        然后设置解析状态的回调，比如解析道 URL 时回调，
        解析到 header 时回调。
        接着传入报文执行 llhttp_execute 
    
    */
    int on_message_begin();
    int on_url(llhttp_t *parser, const char *at, size_t length);
    int on_header_field(const char *at, size_t length);
    int on_header_value(const char *at, size_t length);
    int on_headers_complete();
    int on_body(const char *at, size_t len);
    int on_message_complete();


public:
    explicit HTTPServer(bool UseRedis = false, bool UseMultiThread = true);
    ~HTTPServer();
    HTTPServer(HTTPServer& server) = delete;
    HTTPServer(HTTPServer&& server) = delete;
    HTTPServer& operator=(HTTPServer& server) = delete;
    HTTPServer& operator=(HTTPServer&& server) = delete;
    void run(); // http实际启用函数
    int setNewPort(int newPort);
    int setNewBacklog(int newBacklog); // 设定新的完成队列的大小


public:
    // 回调函数部分
    static int callback_on_message_begin(llhttp_t *parser) {
        return HS->on_message_begin();
    }

    static int callback_on_url(llhttp_t *parser, const char *at, size_t length) {
        return HS->on_url(parser, at, length);
    }

    static int callback_on_header_field(llhttp_t *parser, const char *at, size_t length) {
        return HS->on_header_field(at, length);
    }

    static int callback_on_header_value(llhttp_t *parser, const char *at, size_t length) {
        return HS->on_header_value(at, length);
    }

    static int callback_on_headers_complete(llhttp_t *parser) {
        return HS->on_headers_complete();
    }

    static int callback_on_body(llhttp_t *parser, const char *at, size_t length) {
        return HS->on_body(at, length);
    }

    static int callback_on_message_complete(llhttp_t *parser) {
        return HS->on_message_complete();
    }
};

/* 
    结构体一些说明：
    {
        struct sockaddr_in 用来处理网络通信的地址，结构体的定义如下：
            struct sockaddr_in
            {
                __SOCKADDR_COMMON (sin_);
                in_port_t sin_port;			
                struct in_addr sin_addr;		
            
                unsigned char sin_zero[sizeof (struct sockaddr)
                        - __SOCKADDR_COMMON_SIZE
                        - sizeof (in_port_t)
                        - sizeof (struct in_addr)];
            };
    }
    
    {
        什么是backlog? ->答案：该参数指定的是完成队列的长度。

        在TCP建立连接阶段，内核维护着两个队列：

        （1）未完成队列 这是客户端发送SYN过来，服务器回应SYN+ACK之后，
            服务器当前处于SYN_RECV状态，此时的连接在未完成队列中。

        （2）完成队列 客户端回应ACK之后，两边都处于ESTABLISHED状态，此时连接从未完成队列移到完成队列中，
            服务器调用accept，就从完成队列移除并返回给程序。
            
        假如指定了一个很小的backlog，比如1，那么完成队列很容易就满，满了以后客户端连接进来会怎么样呢？
        从上面可知，客户端connect还是成功返回，但是服务器这个连接进不了完成队列，一段时间后被内核释放了，
        服务器就没有办法通过accept得到连接
    }
*/



#endif